/**
 * Flogger - Logging Console for ActionScript
 * Copyright (c) 2010-2011 Ariel Flesler | aflesler(at)gmail(dot)com
 * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
 * Date: 2/2/2011
 *
 * @author Ariel Flesler
 * @version 1.1.0.1
 */

package com.flesler.flogger
{
	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.SecurityErrorEvent;
	import flash.events.StatusEvent;
	import flash.net.LocalConnection;
	import flash.utils.getTimer;
	
	/**
	 * Sends the logs to an external flash player using a LocalConnection
	 */
	public final class Flogger
	{
		private static const CONNECTION_OUT_NAME:String = '_flogger_log';
		private static const CONNECTION_IN_NAME:String = '_flogger_cmd';
		
		public static const DEBUG:String = 'debug';
		public static const INFO:String = 'info';
		public static const WARN:String = 'warn';
		public static const ERROR:String = 'error';
		public static const FATAL:String = 'fatal';
		
		private static const ERROR_STATUS:String = 'error';
		private static const ERROR_TIMEOUT:uint = 5e3;
		private static const COMMAND_HANDLER:String = 'command';
		
		private var outConnection:LocalConnection;
		private var inConnection:LocalConnection;
		
		private var errorStamp:int = -ERROR_TIMEOUT;
		
		public function Flogger()
		{
		}
		
		private function ignore(e:Event):void
		{
		}
		
		private function createConnection():LocalConnection
		{
			var connection:LocalConnection = new LocalConnection();
			connection.allowDomain('*');
			connection.allowInsecureDomain('*');
			connection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, ignore);
			connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, ignore);
			connection.addEventListener(StatusEvent.STATUS, ignore);
			return connection;
		}
		
		private function destroyConnection(connection:LocalConnection):void
		{
			if (!connection)
				return;
			
			connection.removeEventListener(AsyncErrorEvent.ASYNC_ERROR, ignore);
			connection.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, ignore);
			connection.removeEventListener(StatusEvent.STATUS, ignore);
			connection.client = null;
			try	{ connection.close(); }
			catch (err:Error) {}
		}
		
		private function checkStatus(e:StatusEvent):void
		{
			if (e.level === ERROR_STATUS)
				failed();
		}
		
		private function failed():void
		{
			errorStamp = getTimer();
		}
		
		//- Logging
		
		public function append(method:String, msg:String):void
		{
			if (!enabled)
				return;
			
			if (!outConnection) {
				outConnection = createConnection();
				outConnection.addEventListener(StatusEvent.STATUS, checkStatus);
			}
			
			try {
				outConnection.send(CONNECTION_OUT_NAME, method, msg);
			} catch (err:Error) {
				failed();
			}
		}
		
		public function get enabled():Boolean 
		{
			return getTimer() - errorStamp > ERROR_TIMEOUT;
		}
	
		public function debug(msg:String):void 
		{
			append(DEBUG, msg);
		}
		
		public function info(msg:String):void 
		{
			append(INFO, msg);
		}
		
		public function warn(msg:String):void 
		{
			append(WARN, msg);
		}
		
		public function error(msg:String):void 
		{
			append(ERROR, msg);
		}
		
		public function fatal(msg:String):void 
		{
			append(FATAL, msg);
		}
		
		//- Command reception
		
		public function receiveCommands(callback:Function):void
		{
			inConnection = createConnection();
			var client:Object = {};
			client[COMMAND_HANDLER] = callback;
			inConnection.client = client;
			inConnection.connect(CONNECTION_IN_NAME);
		}
		
		public function destroy():void
		{
			destroyConnection(inConnection);
			destroyConnection(outConnection);
		}
	}
}
